#pragma once

#include "lzo/lzoconf.h"
#include <msclr\marshal.h>

#include "CompressionAlgorithm.h"
#include "LZOCompressorHelper.h"
#include "LZOCompressorException.h"

using namespace msclr::interop;
using namespace System;

typedef int (*pCompressDecompressFunction)(const lzo_bytep, lzo_uint, lzo_bytep, lzo_uintp, lzo_voidp);

namespace WaveAccess
{
	namespace LZONet
	{
		///<summary>
		/// LZO compression library .Net managed wrapper class.
		///</summary>
		public ref class LZOCompressor
		{
		private:
			//members
			static String^ m_Version;
			static String^ m_VersionDate;
			static LZOCompressorHelper m_LZOCompressorHelper;
			CompressionAlgorithm m_CompressionAlgorithm;
			pCompressDecompressFunction m_CompressFunction;
			pCompressDecompressFunction m_DecompressFunction;
			//methods
			void Initiate(CompressionAlgorithm algoritm);

		public:
			///<summary>
			///Initializes a new instance of the <seealso cref="WaveAccess::LZONet::LZOCompressor"/> class.
			///</summary>
			///<exception cref="WaveAccess::LZONet::LZOCompressorException">
			///Internal error occurred - can't initialize LZO native functions. See the return code and reason description for more information.
			///</exception>
			///<remarks>
			///Compression algorithm for instance initialized with this constructor is LZO1X1 - member of <seealso cref="WaveAccess::LZONet::CompressionAlgorithm"/>.
			///</remarks>
			LZOCompressor();

			///<summary>
			///Initializes a new instance of the <seealso cref="WaveAccess::LZONet::LZOCompressor"/> class with specified compression algorithm.
			///</summary>
			///<param name="algoritm">
			///A member of <seealso cref="WaveAccess::LZONet::CompressionAlgorithm"/> which represents a compression algorithm used by this instance of <seealso cref="WaveAccess::LZONet::LZOCompressor"/>.
			///</param>
			///<exception cref="WaveAccess::LZONet::LZOCompressorException">
			///Internal error occurred - can't initialize LZO native functions. See the return code and reason description for more information.
			///</exception>
			LZOCompressor(CompressionAlgorithm algoritm);

			///<summary>
			///Compresses a data from <seealso cref="System::Array"/> of <seealso cref="System::Byte"/> represented by <paramref name="srcArray"/>.
			///Last four bytes from output array represent <paramref name="srcArray"/> array length and used by decompress function.
			///</summary>
			///<param name="srcArray">
			///An <seealso cref="System::Array"/> of <seealso cref="System::Byte"/> which represents a data to be compressed.
			///</param>
			///<returns>
			///An <seealso cref="System::Array"/> of <seealso cref="System::Byte"/> which represents a compressed <paramref name="srcArray"/> data.
			///</returns>
			///<exception cref="WaveAccess::LZONet::LZOCompressorException">
			///Internal error occurred - can't compress the <paramref name="srcArray"/>. See the return code and reason description for more information.
			///</exception>
			array<Byte>^ Compress(array<Byte>^ srcArray);

			///<summary>
			///Decompresses a data from <seealso cref="System::Array"/> of <seealso cref="System::Byte"/> represented by <paramref name="srcArray"/>.
			///</summary>
			///<param name="srcArray">
			///An <seealso cref="System::Array"/> of <seealso cref="System::Byte"/> which represents a data to be decompressed.
			///</param>
			///<returns>
			///An <seealso cref="System::Array"/> of <seealso cref="System::Byte"/> which represents a decompressed <paramref name="srcArray"/> data.
			///</returns>
			///<exception cref="WaveAccess::LZONet::LZOCompressorException">
			///Internal error occurred - can't decompress the <paramref name="srcArray"/>. See the return code and reason description for more information.
			///</exception>
			///<remarks>
			///This function performs safe decompression with overrun testing - i.e. checks if destination <seealso cref="System::Array"/> of <seealso cref="System::Byte"/>
			///can accommodate the decompressed data.
			///Last four bytes from <paramref name="srcArray"/> must represent original array length and used by decompress function to determine output array length.
			///</remarks>
			array<Byte>^ Decompress(array<Byte>^ srcArray);

			///<summary>
			///Decompresses a data from <seealso cref="System::Array"/> of <seealso cref="System::Byte"/> represented by <paramref name="srcArray"/>.			
			///</summary>
			///<param name="srcArray">
			///An <seealso cref="System::Array"/> of <seealso cref="System::Byte"/> which represents a data to be decompressed.
			///</param>
			///<param name="safe">
			///A <seealso cref="System::Boolean"/> value which tells  this function to perform a safe decompression - i.e. check if 
			///destination <seealso cref="System::Array"/> of <seealso cref="System::Byte"/> can accommodate the decompressed data.
			///</param>
			///<returns>
			///An <seealso cref="System::Array"/> of <seealso cref="System::Byte"/> which represents a decompressed <paramref name="srcArray"/> data.
			///</returns>
			///<exception cref="WaveAccess::LZONet::LZOCompressorException">
			///Internal error occurred - can't decompress the <paramref name="srcArray"/>. See the return code and reason description for more information.
			///</exception>
			///<remarks>
			///Last four bytes from <paramref name="srcArray"/> must represent original array length and used by decompress function to determine output array length.
			///</remarks>
			array<Byte>^ Decompress(array<Byte>^ srcArray, bool safe);
			
			///<summary>
			///Returns <seealso cref="System::String"/> which represents a full version  of <seealso cref="WaveAccess::LZONet::LZOCompressor"/>.
			///</summary>
			///<summary>
			///Returns <seealso cref="System::String"/> which represents a full version  of <seealso cref="WaveAccess::LZONet::LZOCompressor"/>.
			///</summary>
			///<returns>
			///A <seealso cref="System::String"/> which represents a full version  of <seealso cref="WaveAccess::LZONet::LZOCompressor"/>.
			///</returns>
			virtual String^ ToString() override;

			///<summary>
			///Returns <seealso cref="System::String"/> which represents a full version  of <seealso cref="WaveAccess::LZONet::LZOCompressor"/>.
			///</summary>
			///<returns>
			///A <seealso cref="System::String"/> which represents a full version  of <seealso cref="WaveAccess::LZONet::LZOCompressor"/>.
			///</returns>
			property static String^ FullVersion
			{
				String^ get()
				{
					if(m_Version == nullptr)
						m_Version = marshal_as<String^>(lzo_version_string());
					if(m_VersionDate == nullptr)
						m_VersionDate = marshal_as<String^>(lzo_version_date());
					return String::Format("WaveAccess LZONet. Built over native LZO version {0} ({1}).", m_Version, m_VersionDate);
				}
			}

			///<summary>
			///Returns <seealso cref="System::String"/> which represents a short version  of <seealso cref="WaveAccess::LZONet::LZOCompressor"/>.
			///</summary>
			///<returns>
			///A <seealso cref="System::String"/> which represents a short version  of <seealso cref="WaveAccess::LZONet::LZOCompressor"/>.
			///</returns>
			property static String^ Version
			{
				String^ get()
				{
					if(m_Version == nullptr)
						m_Version = marshal_as<String^>(lzo_version_string());
					if(m_VersionDate == nullptr)
						m_VersionDate = marshal_as<String^>(lzo_version_date());
					return String::Format("{0} ({1}).", m_Version, m_VersionDate);
				}
			}
		};
	}
}
